/**
 * 数组扁平化 flat(deepindex)
 * @param {number} deepindex
 * @return {Array}
 */

Array.prototype._flat = function (deepIndex = 1) {
  //非数组调用报错
  if (!Array.isArray(this)) {
    throw Error(`${this}._flat is not a function`);
  }
  //deepIndex参数错误返回原数组
  if (!Number(deepIndex) || deepIndex <= 0) {
    return this;
  }
  let resArry = [...this];
  while (deepIndex) {
    if (resArry.some((x) => Array.isArray(x))) {
      resArry = resArry.reduce(
        (preValue, currentValue) => preValue.concat(currentValue),
        []
      );
      deepIndex--;
    } else break;
  }
  return resArry;
};

/**
 * reduce reduce(callback(preValue,currentValue,index,thisArry),initValue)
 * @param {Function} callback(preValue,currentValue,index,thisArry)
 * @param {*} initValue
 * @return {*}
 */

Array.prototype._reduce = function (callback, initValue) {
  // 异常处理
  // callback必须为函数
  if (typeof callback !== "function") {
    throw Error("parmas error");
  }
  // 必须为数组调用
  if (!Array.isArray(this)) {
    throw Error(`${this} is not a Array`);
  }
  // 初始值和数组只能有一个为空
  if (this.length === 0 && arguments.length === 1) {
    throw Error(`初始值和${this}只能有一个为空`);
  }
  let currentArry = this;
  let resValue = arguments.length === 1 ? currentArry[0] : initValue;
  let index = arguments.length === 1 ? 1 : 0;
  for (index; index < currentArry.length; index++) {
    resValue = callback(resValue, currentArry[index], index, currentArry);
  }
  return resValue;
};

/**
 * reduce实现map
 * @param {Function} callback(preValue,currentValue,index,thisArry)
 * @param {this} thisArgs
 * @return {Array}
 */
Array.prototype.mapByReduce = function (callback, thisArgs) {
  // 异常判断
  if (!Array.isArray(this)) {
    throw Error(`${this} is not Array`);
  }
  if (typeof callback !== "function") {
    throw Error("parmas error");
  }
  thisArgs = thisArgs ? thisArgs : window;
  let currentArry = this;
  let resArry = currentArry.reduce((preValue, currentValue, index, array) => {
    return preValue.concat(callback.call(thisArgs, currentValue, index, array));
  }, []);
  return resArry;
};

/**
 * map map(callback(currentValue,index,arry),thisArgs)
 *@param {Function} callback
 *@param {this} thisArgs
 *@return {Array}
 */
Array.prototype._map = function (callback, thisArgs) {
  //异常处理
  if (!Array.isArray(this)) {
    throw Error(`${this} is not Array`);
  }
  if (typeof callback != "function") {
    throw Error("parmas error");
  }
  thisArgs = thisArgs ? thisArgs : window;
  let currentArry = this;
  let resArry = [];
  for (let index = 0; index < currentArry.length; index++) {
    resArry = resArry.concat(
      callback.call(thisArgs, currentArry[index], index, currentArry)
    );
  }
  return resArry;
};

/**
 * filter  filter(callback(currentVlue,index,array),thisArgs)
 *@param {callback}
 *@param {this} thisArgs
 *@return {Array}
 */
Array.prototype._filter = function (callback, thisArgs) {
  //异常判断
  if (typeof callback != "function") {
    throw Error(`${callback} is not a function`);
  }
  thisArgs = thisArgs ? thisArgs : window;
  let resArry = [];
  let currentArry = this;
  for (let index = 0; index < currentArry.length; index++) {
    if (callback.call(thisArgs, currentArry[index], index, thisArgs)) {
      resArry = resArry.concat(currentArry[index]);
    }
  }
  return resArry;
};

/**
 * 数组去重
 *@return {Array}
 */

Array.prototype.uniqueArray = function () {
  let currentArry = this;
  let obj = {};
  let resArry = [];
  for (let index = 0; index < currentArry.length; index++) {
    let key = typeof currentArry[index] + JSON.stringify(currentArry[index]);
    if (!obj[key]) {
      obj[key] = key;
      resArry.push(currentArry[index]);
    }
  }
  return resArry;
};

/* 类数组转换为数组 */
//[...leishuzu]

/**
 * forEach forEach(callback(currentValue,index,array),thisArgs)
 * @param {Function} callback
 * @param {this} thisArgs
 * @return {undefined}
 */
Array.prototype._forEach = function (callback, thisArgs) {
  //异常判断
  if (typeof callback != "function") {
    throw Error(`${callback} is not a function`);
  }
  thisArgs = thisArgs ? thisArgs : window;
  let currentArry = this;
  for (let index = 0; index < currentArry.length; index++) {
    callback.call(thisArgs, currentArry[index], index, currentArry);
  }
};

/* call call(this,...args) */
Function.prototype._call = function (thisArgs, ...args) {
  //兼容处理
  let context = thisArgs || window;
  context.Fn = this;
  let reslut = context.Fn(...args);
  delete context.Fn;
  return reslut;
};

/* apply apply(this,[...args]) */

Function.prototype._apply = function (thisArgs, args) {
  let context = thisArgs || window;
  context.Fn = this;
  let res = context.Fn(...arg);
  delete context.Fn;
  return res;
};

/* bind bind(this,...args) */

Function.prototype._bind = function (thisArgs, ...argsPartOne) {
  let Fn = this;
  let newFunctin = function (...argsPartTwo) {
    if (this instanceof F) {
      return Fn.apply(this, [...argsPartOne, ...argsPartTwo]);
    }
    return Fn.apply(thisArgs, [...argsPartOne, ...argsPartTwo]);
  };
  const F = function () {};
  F.prototype = this.prototype;
  newFunctin.prototype = new F();
  return newFunctin;
};

/* 实现new */

function myNew(Fn, ...args) {
  let tempObj = {};
  tempObj.__proto__ = Fn.prototype;
  tempObj.__proto__.constructor = Fn;
  let res = Fn.call(tempObj, ...args);
  return Object.prototype.toString.call(res) === "[object Object]"
    ? res
    : tempObj;
}

/* 实现防抖 和 节流 */
function debounce(callback, delay) {
  let debounceFlag = null;
  return function () {
    if (debounceFlag) {
      clearTimeout(debounceFlag);
    }
    debounceFlag = setTimeout(() => {
      callback();
    }, delay);
  };
}

function throttle(callback, delay) {
  let throttleFlag = false;
  return function () {
    if (!throttleFlag) {
      setTimeout(() => {
        callback();
        throttleFlag = true;
      }, delay);
    }
    return;
  };
}

/* 深拷贝 */
function deepCopy(origin) {
  let target = Array.isArray(origin) ? [] : {};
  for (const key in origin) {
    if (Object.hasOwnProperty.call(origin, key)) {
      if (typeof origin[key] != "object") {
        target[key] = origin[key];
      } else {
        target[key] = deepCopy(origin[key]);
      }
    }
  }
  return target;
}

//

/* 柯里化 */
// add(1)(2)(3) = 6

//简单版
// const add = x=>y=>z=>x+y+z
function add(a) {
  return function (b) {
    return function (c) {
      return a + b + c;
    };
  };
}
//完整版
function add() {
  // 第一次执行时，定义一个数组专门用来存储所有的参数
  var _args = Array.prototype.slice.call(arguments);

  // 在内部声明一个函数，利用闭包的特性保存_args并收集所有的参数值
  var _adder = function () {
    _args.push(...arguments);
    return _adder;
  };

  // 利用toString隐式转换的特性，当最后执行时隐式转换，并计算最终的值返回
  _adder.toString = function () {
    return _args.reduce(function (a, b) {
      return a + b;
    });
  };
  return _adder;
}

/* 类型判断 */
function myTypeOf(target) {
  let tempType = Object.prototype.toString.call(target);
  let resType = tempType.slice(8, tempType.length - 1);
  return resType;
}

/* 数组去重 */

/* 继承 */

//父类
function Person(name) {
  this.name = name;
  this.arr = [1];
  this.sayName = () => {
    console.log(this.name);
  };
}
Person.prototype.age = 40;

// 原型链继承
// Son1可以修改父类原型上的属性
// 无法给父类传参
function Son1() {
  this.name = "son1";
}
Son1.prototype = new Person();
Son1.prototype.constructor = Son1;
let son1 = new Son1();

// 构造函数继承
// 只能继承父类构造函数的属性。
// 每个实例都要调用父类函数
function Son2(name) {
  Person.call(this, name);
}
let son2 = new Son2("son2");

// 组合继承
//可以继承父类原型上的属性，可以传参，可复用。
//每个新实例引入的构造函数属性是私有的。
// 调用了两次父类构造函数（耗内存），子类的构造函数会代替原型上的那个父类构造函数。调用了两次父类构造函数（耗内存），子类的构造函数会代替原型上的那个父类构造函数。调用了两次父类构造函数（耗内存），子类的构造函数会代替原型上的那个父类构造函数。
function Son3(name) {
  Person.call(this, name);
}
Son3.prototype = new Person();
let son3 = new Son3("son3");

// 组合寄生优化 调用一次父类构造器
function Son4(name) {
  Person.call(this, name);
}
Son4.prototype = Person.prototype;
Son4.prototype.constructor = Son4;
let son4 = new Son4("son4");

// 寄生组合
function Son5(name) {
  Person.call(this, name);
}
Son5.prototype = Object.create(Person.prototype);
Son5.prototype.constructor = Son5;
let son5 = new Son5("son5");

function MyInstanceof(left, right) {
  // #1 判断 obj 是否为基础类型
  // #2 判断 obj.__proto__ === _constructor.prototype

  if (typeof left !== "object" || left === null || right === null) {
    return false;
  }
  let L = left.__proto__;
  let R = right.prototype;
  while (L) {
    if (L === R) {
      return true;
    }
    L = L.__proto__;
  }

  return false;
}

// function cartesian(arr) {
//   if (arr.length < 2) return arr[0] || [];
//   return [].reduce.call(arr, function (pre, cur) {
//     let res = [];
//     col.forEach((c) => {
//       set.forEach((s) => {
//         let t = [].concat(Array.isArray(c) ? c : [c]);
//         t.push(s);
//         res.push(t);
//       });
//     });
//     return res;
//   });
// }

function cartesian(arr) {
  if (arr.length < 2) return arr[0] || [];
  let resCartesian = arr.reduce((pre, cur) => {
    let res = [];
    pre.forEach((t) => {
      cur.forEach((s) => {
        let temp = [].concat(t);
        temp.push(s);
        res.push(temp);
      });
    });
    return res;
  });
  return resCartesian;
}
